/*
存在解引用无效指针的风险，在主线程如果任务结束的处理先于任务其他状态更新的处理，会存在悬垂指针
*/


#include "Task.h"
#include "App.h"

#define GND_LOCK m_taskLock.lock()
#define GND_UNLOCK m_taskLock.unlock()

namespace Gnodroid {
    //用于产生任务UID的全局变量，只能在主线程使用
    long __task_uid__ = 1;

    Task::Task(TaskProgressType type)
    {
        m_lUid = __task_uid__;
        __task_uid__ ++;

        m_taskProgress.current = 0;
        m_taskProgress.total = 100;

        m_taskType = type;
        //增加一个引用计数
        ref();
        //m_label = "preparing";
    }


    //只在主线程产生，不用加锁
    long
    Task::getId() {return m_lUid;}

    TaskState
    Task::getState() {
        TaskState curState;
        GND_LOCK;
        curState = m_taskState;
        GND_UNLOCK;
        return curState;
    }

    TaskProgress
    Task::getProgress() {
        TaskProgress curProgress;
        GND_LOCK;
        curProgress = m_taskProgress;
        GND_UNLOCK;
        return curProgress;
    }

    std::string
    Task::getLabel() {
        std::string curLabel;
        GND_LOCK;
        curLabel = m_label;
        GND_UNLOCK;
        return curLabel;
    }

    std::string
    Task::getMessage() {
        std::string curMes;
        GND_LOCK;
        curMes = m_mes;
        GND_UNLOCK;
        return curMes;
    }

    TaskProgressType
    Task::getProcessType() {
        return m_taskType;
    }

    bool
    Task::canInterrupt() {return false;}

    bool
    Task::interrupt() {return false;}

    sigc::signal<void, long, TaskState>
    Task::signal_state_changed() {return m_signalStateChanged;}

    sigc::signal<void, long, TaskProgress>
    Task::signal_progress_updated() {return m_signalProgressUpdated;}

    sigc::signal<void, long, std::string>
    Task::signal_label_changed() {return m_signalLabelChanged;}

    sigc::signal<void, long, std::string>
    Task::signal_message_changed() {return m_signalMessageChanged;}



    bool
    Task::updateState(TaskState state)
    {
        GND_LOCK;
        m_taskState = state;
        GND_UNLOCK;

        auto context = App::getCurrentApp()->getMainContext();
        ref();
        context->invoke([this, state]()-> bool {
                            signal_state_changed().emit(getId(), state);
                            unref();
                            return false;
                        });

        return true;
    }

    bool
    Task::updateProgress(TaskProgress progress)
    {
        GND_LOCK;
        m_taskProgress = progress;
        GND_UNLOCK;

        auto context = App::getCurrentApp()->getMainContext();
        ref();
        context->invoke([this, progress]()-> bool {
                            signal_progress_updated().emit(getId(), progress);
                            unref();
                            return false;
                        });

        return true;
    }

    bool
    Task::updateProgress(int current, int total)
    {
        TaskProgress tPro;
        tPro.current = current;
        tPro.total = total;

        return updateProgress(tPro);
    }

    bool
    Task::updateLabel(std::string label)
    {
        GND_LOCK;
        m_label = label;
        GND_UNLOCK;

        auto context = App::getCurrentApp()->getMainContext();
        ref();
        context->invoke([this, label]()-> bool {
                            signal_label_changed().emit(getId(), label);
                            unref();
                            return false;
                        });
        LOG(std::string("update task label: ") + label);

        return true;
    }



    bool
    Task::updateMessage(std::string mes)
    {
        GND_LOCK;
        m_mes = mes;
        GND_UNLOCK;

        auto context = App::getCurrentApp()->getMainContext();
        ref();
        context->invoke([this, mes]()-> bool {
                            signal_message_changed().emit(getId(), mes);
                            unref();
                            return false;
                        });
        LOG(std::string("update task message: ") + mes);

        return true;
    }



    int
    Task::ref()
    {
        int c;
        GND_LOCK;
        m_iRefCount ++;
        c = m_iRefCount;
        GND_UNLOCK;

        return c;
    }

    int Task::unref()
    {
        int c;
        GND_LOCK;
        m_iRefCount --;
        c = m_iRefCount;
        GND_UNLOCK;

        if (!c) {
            LOG_WARN(std::string("delete task: ") + std::to_string(m_lUid));
            delete this;
        }

        return c;
    }
}
